/*
 * Copyright (C) 2005 Luca Veltri - University of Parma - Italy
 *
 * This file is part of MjSip (http://www.mjsip.org)
 *
 * MjSip is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * MjSip is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MjSip; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author(s):
 * Luca Veltri (luca.veltri@unipr.it)
 */

package org.zoolu.sip.message;


import org.zoolu.sip.provider.*;
import org.zoolu.sip.header.*;
import org.zoolu.sip.address.*;
import org.zoolu.sip.message.SipMethods;
import org.zoolu.net.UdpPacket;
import java.util.*;


/** Class BaseMessage implements a generic SIP Message.
 */
public abstract class BaseMessage extends org.zoolu.tools.MonitoredObject
{
    /** UDP */
    public static final String PROTO_UDP="udp";
    /** TCP */
    public static final String PROTO_TCP="tcp";
    /** TLS */
    public static final String PROTO_TLS="tls";
    /** SCTP */
    public static final String PROTO_SCTP="sctp";

    /** SIP version String (actually just "SIP/") */
    protected static final String SIP_VERSION="SIP/";

    /** Maximum receiving packet size */
    protected static int MAX_PKT_SIZE=8000;

    /** The remote ip address */
    protected String remote_addr;

    /** The remote port */
    protected int remote_port;

    /** Transport protocol */
    protected String transport_proto;

    /** Connection identifier */
    protected TransportConnId conn_id;

    /** Packet length */
    //protected int packet_length;

    /** The message string */
    protected String message;


    /** Inits empty Message. */
    private void init()
    {  //message="";
        remote_addr=null;
        remote_port=0;
        transport_proto=null;
        conn_id=null;
    }

    /** Costructs a new empty Message. */
    public BaseMessage()
    {  init();
        message="";
    }

    /** Costructs a new Message. */
    public BaseMessage(byte[] data, int offset, int len)
    {  init();
        message=new String(data,offset,len);
    }

    /** Costructs a new Message. */
    public BaseMessage(UdpPacket packet)
    {  init();
        message=new String(packet.getData(),packet.getOffset(),packet.getLength());
    }

    /** Costructs a new Message. */
    public BaseMessage(String str)
    {  init();
        message=new String(str);
    }

    /** Costructs a new Message. */
    public BaseMessage(BaseMessage msg)
    {  //message=new String(msg.message);
        message=msg.message;
        remote_addr=msg.remote_addr;
        remote_port=msg.remote_port;
        transport_proto=msg.transport_proto;
        conn_id=msg.conn_id;
        //packet_length=msg.packet_length;
    }

    /** Creates and returns a clone of this object. */
    abstract public Object clone();
    //{  return new Message(message);
    //}

    /** Sets the entire message. */
    public void setMessage(String message)
    {  this.message=message;
    }

    /** Gets string representation of Message. */
    public String toString()
    {  return message;
    }

    /** Gets remote ip address. */
    public String getRemoteAddress()
    {  return remote_addr;
    }

    /** Gets remote port. */
    public int getRemotePort()
    {  return remote_port;
    }

    /** Gets transport protocol. */
    public String getTransportProtocol()
    {  return transport_proto;
    }

    /** Gets connection identifier. */
    public TransportConnId getTransportConnId()
    {  return conn_id;
    }

    /** Gets message length. */
    public int getLength()
    {  return message.length();
    }

    /** Sets remote ip address. */
    public void setRemoteAddress(String addr)
    {  remote_addr=addr;
    }

    /** Sets remote port. */
    public void setRemotePort(int port)
    {  remote_port=port;
    }

    /** Sets transport protocol. */
    public void setTransport(String proto)
    {  transport_proto=proto;
    }

    /** Sets connection identifier. */
    public void setTransportConnId(TransportConnId conn_id)
    {  this.conn_id=conn_id;
    }

    /** Gets the inique DialogId for an INCOMING message. */
    public DialogId getDialogId()
    {  String call_id=getCallIdHeader().getCallId();
        String local_tag, remote_tag;
        if (isRequest()) {  local_tag=getToHeader().getTag(); remote_tag=getFromHeader().getTag(); }
        else {  local_tag=getFromHeader().getTag(); remote_tag=getToHeader().getTag(); }
        return new DialogId(call_id,local_tag,remote_tag);
    }


    /** Gets the unique client TransactionId. */
    public TransactionId getTransactionClientId()
    {  return getTransactionId(true);
    }

    /** Gets the unique server TransactionId. */
    public TransactionId getTransactionServerId()
    {  return getTransactionId(false);
    }

    /** Gets the unique TransactionId. */
    private TransactionId getTransactionId(boolean is_client)
    {  String call_id=getCallIdHeader().getCallId();
        ViaHeader top_via=getViaHeader();
        String branch=null;
        if (top_via.hasBranch()) branch=top_via.getBranch();
        String sent_by=top_via.getSentBy();
        CSeqHeader cseqh=getCSeqHeader();
        long seqn=cseqh.getSequenceNumber();
        String method=cseqh.getMethod();
        return new TransactionId(call_id,seqn,method,is_client,sent_by,branch);
    }


    /** Gets the MethodId. */
    public MethodId getMethodId()
    {  String method=getCSeqHeader().getMethod();
        return new MethodId(method);
    }


    //**************************** Requests ****************************/

    /** Whether Message is a Request. */
    public boolean isRequest() throws NullPointerException
    {  // Req-Line = Method ' ' SIP-URL ' ' "SIP/2.0" CRLF
        if (message==null || isResponse()) return false;
        String firstline=(new SipParser(message)).getLine();
        String proto_version=(new SipParser(firstline)).skipString().skipString().getString();
        if (proto_version==null || proto_version.length()<SIP_VERSION.length()) return false;
        proto_version=proto_version.substring(0,SIP_VERSION.length());
        if (proto_version.equalsIgnoreCase(SIP_VERSION)) return true;
        return false;
    }

    /** Whether Message is a <i>method</i> request. */
    public boolean isRequest(String method)
    {  //if (message==null) return false;
        if (message.startsWith(method)) return true; else return false;
    }

    /** Whether Message is a Method that creates a dialog. */
    public boolean createsDialog()
    {  if (!isRequest()) return false;
        //else
        String method=getRequestLine().getMethod();
        for (int i=0; i<SipMethods.dialog_methods.length; i++)
            if (method.equalsIgnoreCase(SipMethods.dialog_methods[i])) return true;
        //else
        return false;
    }

    /** Whether Message is an Invite. */
    public boolean isInvite()
    {  return isRequest(SipMethods.INVITE);
    }

    /** Whether Message is a Register. */
    public boolean isRegister()
    {  return isRequest(SipMethods.REGISTER);
    }

    /** Whether Message is a Cancel. */
    public boolean isCancel()
    {  return isRequest(SipMethods.CANCEL);
    }

    /** Whether Message is a Bye. */
    public boolean isBye()
    {  return isRequest(SipMethods.BYE);
    }

    /** Whether Message is an Ack. */
    public boolean isAck()
    {  return isRequest(SipMethods.ACK);
    }

    /** Whether Message is an Info. */
    public boolean isInfo()
    {  return isRequest(SipMethods.INFO);
    }

    /** Whether Message is an Options. */
    public boolean isOptions()
    {  return isRequest(SipMethods.OPTIONS);
    }

    /** Whether Message has Request-line. */
    protected boolean hasRequestLine()
    {  return isRequest();
    }

    /** Gets the RequestLine of the Message (returns null if called for no request message). */
    public RequestLine getRequestLine()
    {  if (!isRequest()) return null;
        SipParser par=new SipParser(message);
        return par.getRequestLine();
    }

    /** Sets the RequestLine of the Message. */
    public void setRequestLine(RequestLine rl)
    {  if (hasRequestLine()) removeRequestLine();
        message=rl.toString()+message;
    }

    /** Removes the RequestLine of the Message. */
    public void removeRequestLine()
    {  if(!isRequest()) return;
        removeFirstLine();
    }


    //**************************** Responses ****************************/

    /** Whether Message is a Response. */
    public boolean isResponse() throws NullPointerException
    {  // Status-Line = "SIP/2.0" ' ' Status-Code ' 'Reason-Phrase" CRLF
        //if (message==null) return false;
        if (message==null || message.length()<SIP_VERSION.length()) return false;
        String proto_version=message.substring(0,SIP_VERSION.length());
        if (proto_version.equalsIgnoreCase(SIP_VERSION)) return true;

        return false;
    }

    /** Whether Message has Status-line. */
    protected boolean hasStatusLine()
    {  return isResponse();
    }

    /** Gets the StautsLine of the Message (returns null if called for no response message). */
    public StatusLine getStatusLine()
    {  if (!isResponse()) return null;
        SipParser par=new SipParser(message);
        return par.getStatusLine();
    }

    /** Sets the StatusLine of the Message. */
    public void setStatusLine(StatusLine sl)
    {  if (hasStatusLine()) removeStatusLine();
        message=sl.toString()+message;
    }

    /** Removes the StatusLine of the Message. */
    public void removeStatusLine()
    {  if(!isResponse()) return;
        removeFirstLine();
    }


    //**************************** Generic Headers ****************************/

    /** Returns the transaction method. */
    public String getTransactionMethod()
    {  return getCSeqHeader().getMethod();
    }

    /** Gets the first line of the Message. */
    public String getFirstLine()
    {  if (isRequest()) return getRequestLine().toString();
        if (isResponse()) return getStatusLine().toString();
        return null;
    }

    /** Sets Request/Status Line of the Message. */
   /*private void setFirstLine(String value)
   {  message=value+" \r\n"+message;
   }. */

    /** Removes Request\Status Line of the Message. */
    protected void removeFirstLine()
    {  message=message.substring((new SipParser(message)).indexOfNextHeader());
    }

    /** Whether Message has any headers of specified rtpmap. */
    public boolean hasHeader(String name)
    {  Header hd=getHeader(name);
        if (hd==null) return false;
        else return true;
    }

    /** Gets the first Header of specified rtpmap (returns null if no Header is found). */
    public Header getHeader(String hname)
    {  SipParser par=new SipParser(message);
        return par.getHeader(hname);
    }

    /** Gets a Vector of all Headers of specified rtpmap (returns empty Vector if no Header is found). */
    public Vector getHeaders(String hname)
    {  Vector v=new Vector();
        SipParser par=new SipParser(message);
        Header h;
        while ((h=par.getHeader(hname))!=null)
        {  v.addElement(h);
        }
        return v;
    }

    /** Adds Header at the top/bottom.
     * The bottom is considered before the Content-Length and Content-Type headers. */
    public void addHeader(Header header, boolean top)
    {  addHeaders(header.toString(),top);
    }

    /** Adds a Vector of Headers at the top/bottom. */
    public void addHeaders(Vector headers, boolean top)
    {  String str="";
        for (int i=0; i<headers.size(); i++) str+=((Header)headers.elementAt(i)).toString();
        addHeaders(str,top);
    }

    /** Adds MultipleHeader(s) <i>mheader</i> at the top/bottom. */
    public void addHeaders(MultipleHeader mheader, boolean top)
    {  addHeaders(mheader.toString(),top);
    }

    /** Adds a one or more Headers at the top/bottom.
     * The bottom is considered before the Content-Length and Content-Type headers. */
    protected void addHeaders(String str, boolean top)
    {  int i,aux;
        if (top)
        {  if (this.hasRequestLine() || this.hasStatusLine())
        {  SipParser par=new SipParser(message);
            par.goToNextHeader();
            i=par.getPos();
        }
        else i=0;
        }
        else
        {  SipParser par=new SipParser(message);
            // index the end of headers
            i=par.goToEndOfLastHeader().goToNextLine().getPos();
            par=new SipParser(message);
            // if Content_Length is present, jump before
            aux=par.indexOfHeader(SipHeaders.Content_Length);
            if (aux<i) i=aux;
            // if Content_Type is present, jump before
            aux=par.indexOfHeader(SipHeaders.Content_Type);
            if (aux<i) i=aux;
        }
        String head=message.substring(0,i);
        String tail=message.substring(i);
        String new_message=head.concat(str);
        new_message=new_message.concat(tail);
        message=new_message;
    }

    /** Adds Headers on position <i>index</i> within the Message. */
    protected void addHeaders(String str, int index)
    {  if (index>message.length()) index=message.length();
        message=message.substring(0,index)+str+message.substring(index);
    }

    /** Adds Header before the first header <i>refer_header</i>
     * . <p>If there is no header of such type, it is added at top. */
    public void addHeaderBefore(Header new_header, String refer_header)
    {  addHeadersBefore(new_header.toString(),refer_header);
    }

    /** Adds MultipleHeader(s) before the first header <i>refer_header</i>
     * . <p>If there is no header of such type, they are added at top. */
    public void addHeadersBefore(MultipleHeader mheader, String refer_header)
    {  addHeadersBefore(mheader.toString(),refer_header);
    }

    /** Adds Headers before the first header <i>refer_header</i>
     * . <p>If there is no header of such type, they are added at top. */
    protected void addHeadersBefore(String str, String refer_header)
    {  if (!hasHeader(refer_header)) addHeaders(str,true);
    else
    {  SipParser par=new SipParser(message);
        par.goTo(refer_header);
        int here=par.getPos();
        message=message.substring(0,here)+str+message.substring(here);
    }
    }

    /** Adds Header after the first header <i>refer_header</i>
     * . <p>If there is no header of such type, it is added at bottom. */
    public void addHeaderAfter(Header new_header, String refer_header)
    {  addHeadersAfter(new_header.toString(),refer_header);
    }

    /** Adds MultipleHeader(s) after the first header <i>refer_header</i>
     * . <p>If there is no header of such type, they are added at bottom. */
    public void addHeadersAfter(MultipleHeader mheader, String refer_header)
    {  addHeadersAfter(mheader.toString(),refer_header);
    }

    /** Adds Headers after the first header <i>refer_header</i>
     * . <p>If there is no header of such type, they are added at bottom. */
    protected void addHeadersAfter(String str, String refer_header)
    {  if (!hasHeader(refer_header)) addHeaders(str,false);
    else
    {  SipParser par=new SipParser(message);
        par.goTo(refer_header);
        int here=par.indexOfNextHeader();
        message=message.substring(0,here)+str+message.substring(here);
    }
    }

    /** Removes first Header of specified rtpmap. */
    public void removeHeader(String hname)
    {  removeHeader(hname,true);
    }

    /** Removes first (or last) Header of specified rtpmap. */
    public void removeHeader(String hname, boolean first)
    {  String[] target={'\n'+hname, '\r'+hname};
        SipParser par=new SipParser(message);
        par.goTo(target);
        if (!par.hasMore()) return;
        if (!first)
            while(true)
            {  int next=par.indexOf(target);
                if (next<0) break;
                par.setPos(next);
            }
        par.skipChar();
        String head=message.substring(0,par.getPos());
        par.goToNextHeader();
        String tail=message.substring(par.getPos());
        message=head.concat(tail);
    }

    /** Sets the new Header (removing any previous headers of the same rtpmap). */
   /*public void setHeader(Header hd) 
   {  if (hasHeader(hd.getName())) removeAllHeaders(hd.getName());
      addHeader(hd,false);               
   }*/

    /** Sets the Header <i>hd</i> removing any previous headers of the same type. */
    public void setHeader(Header hd)
    {  String hname=hd.getName();
        if (hasHeader(hname))
        {  int index=(new SipParser(message)).indexOfHeader(hname);
            removeAllHeaders(hname);
            addHeaders(hd.toString(),index);
        }
        else
            addHeader(hd,false);
    }

    /** Removes all Headers of specified rtpmap. */
    public void removeAllHeaders(String hname)
    {  String[] target={'\n'+hname, '\r'+hname};
        SipParser par=new SipParser(message);
        par.goTo(target);
        while (par.hasMore())
        {  par.skipChar();
            String head=message.substring(0,par.getPos());
            String tail=message.substring(par.indexOfNextHeader());
            message=head.concat(tail);
            par=new SipParser(message,par.getPos()-1);
            par.goTo(target);
        }
    }

    /** Sets MultipleHeader <i>mheader</i>. */
   /*public void setHeaders(MultipleHeader mheader) 
   {  if (hasHeader(mheader.getName())) removeAllHeaders(mheader.getName());
      addHeaders(mheader,false);            
   }*/

    /** Sets MultipleHeader <i>mheader</i>. */
    public void setHeaders(MultipleHeader mheader)
    {  String hname=mheader.getName();
        if (hasHeader(hname))
        {  int index=(new SipParser(message)).indexOfHeader(hname);
            removeAllHeaders(hname);
            addHeaders(mheader.toString(),index);
        }
        else
            addHeaders(mheader,false);
    }


    //**************************** Specific Headers ****************************/

    /** Whether Message has MaxForwardsHeader. */
    public boolean hasMaxForwardsHeader()
    {  return hasHeader(SipHeaders.Max_Forwards);
    }
    /** Gets MaxForwardsHeader of Message. */
    public MaxForwardsHeader getMaxForwardsHeader()
    {  Header h=getHeader(SipHeaders.Max_Forwards);
        if (h==null) return null;
        else return new MaxForwardsHeader(h);
    }
    /** Sets MaxForwardsHeader of Message. */
    public void setMaxForwardsHeader(MaxForwardsHeader mfh)
    {  setHeader(mfh);
    }
    /** Removes MaxForwardsHeader from Message. */
    public void removeMaxForwardsHeader()
    {  removeHeader(SipHeaders.Max_Forwards);
    }


    /** Whether Message has FromHeader. */
    public boolean hasFromHeader()
    {  return hasHeader(SipHeaders.From);
    }
    /** Gets FromHeader of Message. */
    public FromHeader getFromHeader()
    {  Header h=getHeader(SipHeaders.From);
        if (h==null) return null;
        else return new FromHeader(h);
    }
    /** Sets FromHeader of Message. */
    public void setFromHeader(FromHeader fh)
    {  setHeader(fh);
    }
    /** Removes FromHeader from Message. */
    public void removeFromHeader()
    {  removeHeader(SipHeaders.From);
    }

    /** Whether Message has ToHeader. */
    public boolean hasToHeader()
    {  return hasHeader(SipHeaders.To);
    }
    /** Gets ToHeader of Message. */
    public ToHeader getToHeader()
    {  Header h=getHeader(SipHeaders.To);
        if (h==null) return null;
        else return new ToHeader(h);
    }
    /** Sets ToHeader of Message. */
    public void setToHeader(ToHeader th)
    {  setHeader(th);
    }
    /** Removes ToHeader from Message. */
    public void removeToHeader()
    {  removeHeader(SipHeaders.To);
    }


    /** Whether Message has ContactHeader. */
    public boolean hasContactHeader()
    {  return hasHeader(SipHeaders.Contact);
    }
    /** <b>Deprecated</b>. Gets ContactHeader of Message. Use getContacts instead. */
    public ContactHeader getContactHeader()
    {  //Header h=getHeader(SipHeaders.Contact);
        //if (h==null) return null; else return new ContactHeader(h);
        MultipleHeader mh=getContacts();
        if (mh==null) return null; return new ContactHeader(mh.getTop());
    }
    /** Adds ContactHeader. */
    public void addContactHeader(ContactHeader ch)
    {  addHeader(ch,false);
    }
    /** Sets ContactHeader. */
    public void setContactHeader(ContactHeader ch)
    {  if (hasContactHeader()) removeContacts();
        addHeader(ch,false);
    }
    /** Gets a MultipleHeader of Contacts. */
    public MultipleHeader getContacts()
    {  Vector v=getHeaders(SipHeaders.Contact);
        if (v.size()>0) return new MultipleHeader(v);
        else return null;
    }
    /** Adds Contacts. */
    public void addContacts(MultipleHeader contacts, boolean top)
    {  addHeaders(contacts,top);
    }
    /** Sets Contacts. */
    public void setContacts(MultipleHeader contacts)
    {  if (hasContactHeader()) removeContacts();
        addContacts(contacts,false);
    }
    /** Removes ContactHeaders from Message. */
    public void removeContacts()
    {  removeAllHeaders(SipHeaders.Contact);
    }


    /** Whether Message has ViaHeaders. */
    public boolean hasViaHeader()
    {  return hasHeader(SipHeaders.Via);
    }
    /** Adds ViaHeader at the top. */
    public void addViaHeader(ViaHeader vh)
    {  addHeader(vh,true);
    }
    /** Gets the top ViaHeader. */
    public ViaHeader getViaHeader()
    {  //Header h=getHeader(SipHeaders.Via);
        //if (h==null) return null; else return new ViaHeader(h);
        MultipleHeader mh=getVias();
        if (mh==null) return null; return new ViaHeader(mh.getTop());
    }
    /** Removes the top ViaHeader. */
    public void removeViaHeader()
    {  //removeHeader(SipHeaders.Via);
        MultipleHeader mh=getVias();
        mh.removeTop();
        setVias(mh);
    }
    /** Gets all Vias. */
    public MultipleHeader getVias()
    {  Vector v=getHeaders(SipHeaders.Via);
        if (v.size()>0) return new MultipleHeader(v);
        else return null;
    }
    /** Adds Vias. */
    public void addVias(MultipleHeader vias, boolean top)
    {  addHeaders(vias,top);
    }
    /** Sets Vias. */
    public void setVias(MultipleHeader vias)
    {  if (hasViaHeader()) removeVias();
        addContacts(vias,true);
    }
    /** Removes ViaHeaders from Message (if any exists). */
    public void removeVias()
    {  removeAllHeaders(SipHeaders.Via);
    }


    /** Whether Message has RouteHeader. */
    public boolean hasRouteHeader()
    {  return hasHeader(SipHeaders.Route);
    }
    /** Adds RouteHeader at the top. */
    public void addRouteHeader(RouteHeader h)
    {  addHeaderAfter(h,SipHeaders.Via);
    }
    /** Adds multiple Route headers at the top. */
    public void addRoutes(MultipleHeader routes)
    {  addHeadersAfter(routes,SipHeaders.Via);
    }
    /** Gets the top RouteHeader. */
    public RouteHeader getRouteHeader()
    {  //Header h=getHeader(SipHeaders.Route);
        //if (h==null) return null; else return new RouteHeader(h);
        MultipleHeader mh=getRoutes();
        if (mh==null) return null; return new RouteHeader(mh.getTop());
    }
    /** Gets the whole route. */
    public MultipleHeader getRoutes()
    {  Vector v=getHeaders(SipHeaders.Route);
        if (v.size()>0) return new MultipleHeader(v);
        else return null;
    }
    /** Removes the top RouteHeader. */
    public void removeRouteHeader()
    {  //removeHeader(SipHeaders.Route);
        MultipleHeader mh=getRoutes();
        mh.removeTop();
        setRoutes(mh);
    }
    /** Removes all RouteHeaders from Message (if any exists). */
    public void removeRoutes()
    {  removeAllHeaders(SipHeaders.Route);
    }
    /** Sets the whole route. */
    public void setRoutes(MultipleHeader routes)
    {  if (hasRouteHeader()) removeRoutes();
        addRoutes(routes);
    }


    /** Whether Message has RecordRouteHeader. */
    public boolean hasRecordRouteHeader()
    {  return hasHeader(SipHeaders.Record_Route);
    }
    /** Adds RecordRouteHeader at the top. */
    public void addRecordRouteHeader(RecordRouteHeader rr)
    {  //addHeaderAfter(rr,SipHeaders.Via);
        addHeaderAfter(rr,SipHeaders.CSeq);
    }
    /** Adds multiple RecordRoute headers at the top. */
    public void addRecordRoutes(MultipleHeader routes)
    {  //addHeadersAfter(routes,SipHeaders.Via);
        addHeadersAfter(routes,SipHeaders.CSeq);
    }
    /** Gets the top RecordRouteHeader. */
    public RecordRouteHeader getRecordRouteHeader()
    {  //Header h=getHeader(SipHeaders.Record_Route);
        //if (h==null) return null; else return new RecordRouteHeader(h);
        MultipleHeader mh=getRecordRoutes();
        if (mh==null) return null; return new RecordRouteHeader(mh.getTop());
    }
    /** Gets the whole RecordRoute headers. */
    public MultipleHeader getRecordRoutes()
    {  Vector v=getHeaders(SipHeaders.Record_Route);
        if (v.size()>0) return new MultipleHeader(v);
        else return null;
    }
    /** Removes the top RecordRouteHeader. */
    public void removeRecordRouteHeader()
    {  //removeHeader(SipHeaders.Record_Route);
        MultipleHeader mh=getRecordRoutes();
        mh.removeTop();
        setRecordRoutes(mh);
    }
    /** Removes all RecordRouteHeader from Message (if any exists). */
    public void removeRecordRoutes()
    {  removeAllHeaders(SipHeaders.Record_Route);
    }
    /** Sets the whole RecordRoute headers. */
    public void setRecordRoutes(MultipleHeader routes)
    {  if (hasRecordRouteHeader()) removeRecordRoutes();
        addRecordRoutes(routes);
    }


    /** Whether Message has CSeqHeader. */
    public boolean hasCSeqHeader()
    {  return hasHeader(SipHeaders.CSeq);
    }
    /** Gets CSeqHeader of Message. */
    public CSeqHeader getCSeqHeader()
    {  Header h=getHeader(SipHeaders.CSeq);
        if (h==null) return null;
        else return new CSeqHeader(h);
    }
    /** Sets CSeqHeader of Message. */
    public void setCSeqHeader(CSeqHeader csh)
    {  setHeader(csh);
    }
    /** Removes CSeqHeader from Message. */
    public void removeCSeqHeader()
    {  removeHeader(SipHeaders.CSeq);
    }


    /** Whether has CallIdHeader. */
    public boolean hasCallIdHeader()
    {  return hasHeader(SipHeaders.Call_ID);
    }
    /** Sets CallIdHeader of Message. */
    public void setCallIdHeader(CallIdHeader cih)
    {  setHeader(cih);
    }
    /** Gets CallIdHeader of Message. */
    public CallIdHeader getCallIdHeader()
    {  Header h=getHeader(SipHeaders.Call_ID);
        if (h==null) return null;
        else return new CallIdHeader(h);
    }
    /** Removes CallIdHeader from Message. */
    public void removeCallIdHeader()
    {  removeHeader(SipHeaders.Call_ID);
    }


    /** Whether Message has SubjectHeader. */
    public boolean hasSubjectHeader()
    {  return hasHeader(SipHeaders.Subject);
    }
    /** Sets SubjectHeader of Message. */
    public void setSubjectHeader(SubjectHeader sh)
    {  setHeader(sh);
    }
    /** Gets SubjectHeader of Message. */
    public SubjectHeader getSubjectHeader()
    {  Header h=getHeader(SipHeaders.Subject);
        if (h==null) return null;
        else return new SubjectHeader(h);
    }
    /** Removes SubjectHeader from Message. */
    public void removeSubjectHeader()
    {  removeHeader(SipHeaders.Subject);
    }


    /** Whether Message has DateHeader. */
    public boolean hasDateHeader()
    {  return hasHeader(SipHeaders.Date);
    }
    /** Gets DateHeader of Message. */
    public DateHeader getDateHeader()
    {  Header h=getHeader(SipHeaders.Date);
        if (h==null) return null;
        else return new DateHeader(h);
    }
    /** Sets DateHeader of Message. */
    public void setDateHeader(DateHeader dh)
    {  setHeader(dh);
    }
    /** Removes DateHeader from Message (if it exists). */
    public void removeDateHeader()
    {  removeHeader(SipHeaders.Date);
    }


    /** Whether has UserAgentHeader. */
    public boolean hasUserAgentHeader()
    {  return hasHeader(SipHeaders.User_Agent);
    }
    /** Sets UserAgentHeader. */
    public void setUserAgentHeader(UserAgentHeader h)
    {  setHeader(h);
    }
    /** Gets UserAgentHeader. */
    public UserAgentHeader getUserAgentHeader()
    {  Header h=getHeader(SipHeaders.User_Agent);
        if (h==null) return null;
        else return new UserAgentHeader(h);
    }
    /** Removes UserAgentHeader. */
    public void removeUserAgentHeader()
    {  removeHeader(SipHeaders.User_Agent);
    }


    /** Whether has ServerHeader. */
    public boolean hasServerHeader()
    {  return hasHeader(SipHeaders.Server);
    }
    /** Sets ServerHeader. */
    public void setServerHeader(ServerHeader h)
    {  setHeader(h);
    }
    /** Gets ServerHeader. */
    public ServerHeader getServerHeader()
    {  Header h=getHeader(SipHeaders.Server);
        if (h==null) return null;
        else return new ServerHeader(h);
    }
    /** Removes ServerHeader. */
    public void removeServerHeader()
    {  removeHeader(SipHeaders.Server);
    }


    /** Whether has AcceptHeader. */
    public boolean hasAcceptHeader()
    {  return hasHeader(SipHeaders.Accept);
    }
    /** Sets AcceptHeader. */
    public void setAcceptHeader(AcceptHeader h)
    {  setHeader(h);
    }
    /** Gets AcceptHeader. */
    public AcceptHeader getAcceptHeader()
    {  Header h=getHeader(SipHeaders.Accept);
        if (h==null) return null;
        else return new AcceptHeader(h);
    }
    /** Removes AcceptHeader. */
    public void removeAcceptHeader()
    {  removeHeader(SipHeaders.Accept);
    }


    /** Whether has AlertInfoHeader. */
    public boolean hasAlertInfoHeader()
    {  return hasHeader(SipHeaders.Alert_Info);
    }
    /** Sets AlertInfoHeader. */
    public void setAlertInfoHeader(AlertInfoHeader h)
    {  setHeader(h);
    }
    /** Gets AlertInfoHeader. */
    public AlertInfoHeader getAlertInfoHeader()
    {  Header h=getHeader(SipHeaders.Alert_Info);
        if (h==null) return null;
        else return new AlertInfoHeader(h);
    }
    /** Removes AlertInfoHeader. */
    public void removeAlertInfoHeader()
    {  removeHeader(SipHeaders.Alert_Info);
    }


    /** Whether has AllowHeader. */
    public boolean hasAllowHeader()
    {  return hasHeader(SipHeaders.Allow);
    }
    /** Sets AllowHeader. */
    public void setAllowHeader(AllowHeader h)
    {  setHeader(h);
    }
    /** Gets AllowHeader. */
    public AllowHeader getAllowHeader()
    {  Header h=getHeader(SipHeaders.Allow);
        if (h==null) return null;
        else return new AllowHeader(h);
    }
    /** Removes AllowHeader. */
    public void removeAllowHeader()
    {  removeHeader(SipHeaders.Allow);
    }


    /** Whether Message has ExpiresHeader. */
    public boolean hasExpiresHeader()
    {  return hasHeader(SipHeaders.Expires);
    }
    /** Gets ExpiresHeader of Message. */
    public ExpiresHeader getExpiresHeader()
    {  Header h=getHeader(SipHeaders.Expires);
        if (h==null) return null;
        else return new ExpiresHeader(h);
    }
    /** Sets ExpiresHeader of Message. */
    public void setExpiresHeader(ExpiresHeader eh)
    {  setHeader(eh);
    }
    /** Removes ExpiresHeader from Message (if it exists). */
    public void removeExpiresHeader()
    {  removeHeader(SipHeaders.Expires);
    }


    /** Whether Message has ContentTypeHeader. */
    public boolean hasContentTypeHeader()
    {  return hasHeader(SipHeaders.Content_Type);
    }
    /** Gets ContentTypeHeader of Message. */
    public ContentTypeHeader getContentTypeHeader()
    {  Header h=getHeader(SipHeaders.Content_Type);
        if (h==null) return null;
        else return new ContentTypeHeader(h);
    }
    /** Sets ContentTypeHeader of Message. */
    protected void setContentTypeHeader(ContentTypeHeader cth)
    {  setHeader(cth);
    }
    /** Removes ContentTypeHeader from Message (if it exists). */
    protected void removeContentTypeHeader()
    {  removeHeader(SipHeaders.Content_Type);
    }


    /** Whether Message has ContentLengthHeader. */
    public boolean hasContentLengthHeader()
    {  return hasHeader(SipHeaders.Content_Length);
    }
    /** Gets ContentLengthHeader of Message. */
    public ContentLengthHeader getContentLengthHeader()
    {  Header h=getHeader(SipHeaders.Content_Length);
        if (h==null) return null;
        else return new ContentLengthHeader(h);
    }
    /** Sets ContentLengthHeader of Message. */
    protected void setContentLengthHeader(ContentLengthHeader clh)
    {  setHeader(clh);
    }
    /** Removes ContentLengthHeader from Message (if it exists). */
    protected void removeContentLengthHeader()
    {  removeHeader(SipHeaders.Content_Length);
    }


    /** Whether Message has Body. */
    public boolean hasBody()
    {  if (hasContentLengthHeader()) return getContentLengthHeader().getContentLength()>0;
    else return hasContentTypeHeader();
    }
    /** Gets body(content) type. */
    public String getBodyType()
    {  return getContentTypeHeader().getContentType();
    }
    /** Sets the message body. */
    public void setBody(String content_type, String body)
    {  removeBody();
        if (body!=null && body.length()>0)
        {  setContentTypeHeader(new ContentTypeHeader(content_type));
            setContentLengthHeader(new ContentLengthHeader(body.length()));
            message=message+"\r\n"+body;
        }
        else
        {  setContentLengthHeader(new ContentLengthHeader(0));
            message=message+"\r\n";
        }
    }
    /** Sets sdp body. */
    public void setBody(String body)
    {  setBody("application/sdp",body);
    }
    /** Gets message body. The end of body is evaluated
     * from the Content-Length header if present (SIP-RFC compliant),
     * or from the end of message if no Content-Length header is present (non-SIP-RFC compliant). */
    public String getBody()
    {  //if (!hasBody()) return "";
        if (!hasBody()) return null;
        int begin=(new SipParser(message)).goToBody().getPos();
        int len;
        // the following 'if' is for robustness with non SIP-compliant UAs;
        // copliant UAs must insert Content-Length header when body is present..
        if (this.hasContentLengthHeader()) len=getContentLengthHeader().getContentLength();
        else
        {  //printWarning("No Content-Length header found for the Body",3);
            len=message.length()-begin;
        }
        int end=begin+len;
        if (end>message.length())
        {  //printWarning("Found a Message Body shorter than Content-Length",3);
            end=message.length();
        }
        return message.substring(begin,end);
    }
    /** Removes the message body (if it exists) and the final empty line. */
    public void removeBody()
    {  int pos=(new SipParser(message)).goToEndOfLastHeader().goToNextLine().getPos();
        message=message.substring(0,pos);
        removeContentLengthHeader();
        removeContentTypeHeader();
    }


    //**************************** Authentication ****************************/


    /** Whether has AuthenticationInfoHeader. */
    public boolean hasAuthenticationInfoHeader()
    {  return hasHeader(SipHeaders.Authentication_Info);
    }
    /** Sets AuthenticationInfoHeader. */
    public void setAuthenticationInfoHeader(AuthenticationInfoHeader h)
    {  setHeader(h);
    }
    /** Gets AuthenticationInfoHeader. */
    public AuthenticationInfoHeader getAuthenticationInfoHeader()
    {  Header h=getHeader(SipHeaders.Authentication_Info);
        if (h==null) return null;
        else return new AuthenticationInfoHeader(h);
    }
    /** Removes AuthenticationInfoHeader. */
    public void removeAuthenticationInfoHeader()
    {  removeHeader(SipHeaders.Authentication_Info);
    }


    /** Whether has AuthorizationHeader. */
    public boolean hasAuthorizationHeader()
    {  return hasHeader(SipHeaders.Authorization);
    }
    /** Sets AuthorizationHeader. */
    public void setAuthorizationHeader(AuthorizationHeader h)
    {  setHeader(h);
    }
    /** Gets AuthorizationHeader. */
    public AuthorizationHeader getAuthorizationHeader()
    {  Header h=getHeader(SipHeaders.Authorization);
        if (h==null) return null;
        else return new AuthorizationHeader(h);
    }
    /** Removes AuthorizationHeader. */
    public void removeAuthorizationHeader()
    {  removeHeader(SipHeaders.Authorization);
    }


    /** Whether has WwwAuthenticateHeader. */
    public boolean hasWwwAuthenticateHeader()
    {  return hasHeader(SipHeaders.WWW_Authenticate);
    }
    /** Sets WwwAuthenticateHeader. */
    public void setWwwAuthenticateHeader(WwwAuthenticateHeader h)
    {  setHeader(h);
    }
    /** Gets WwwAuthenticateHeader. */
    public WwwAuthenticateHeader getWwwAuthenticateHeader()
    {  Header h=getHeader(SipHeaders.WWW_Authenticate);
        if (h==null) return null;
        else return new WwwAuthenticateHeader(h);
    }
    /** Removes WwwAuthenticateHeader. */
    public void removeWwwAuthenticateHeader()
    {  removeHeader(SipHeaders.WWW_Authenticate);
    }


    /** Whether has ProxyAuthenticateHeader. */
    public boolean hasProxyAuthenticateHeader()
    {  return hasHeader(SipHeaders.Proxy_Authenticate);
    }
    /** Sets ProxyAuthenticateHeader. */
    public void setProxyAuthenticateHeader(ProxyAuthenticateHeader h)
    {  setHeader(h);
    }
    /** Gets ProxyAuthenticateHeader. */
    public ProxyAuthenticateHeader getProxyAuthenticateHeader()
    {  Header h=getHeader(SipHeaders.Proxy_Authenticate);
        if (h==null) return null;
        else return new ProxyAuthenticateHeader(h);
    }
    /** Removes ProxyAuthenticateHeader. */
    public void removeProxyAuthenticateHeader()
    {  removeHeader(SipHeaders.Proxy_Authenticate);
    }


    /** Whether has ProxyAuthorizationHeader. */
    public boolean hasProxyAuthorizationHeader()
    {  return hasHeader(SipHeaders.Proxy_Authorization);
    }
    /** Sets ProxyAuthorizationHeader. */
    public void setProxyAuthorizationHeader(ProxyAuthorizationHeader h)
    {  setHeader(h);
    }
    /** Gets ProxyAuthorizationHeader. */
    public ProxyAuthorizationHeader getProxyAuthorizationHeader()
    {  Header h=getHeader(SipHeaders.Proxy_Authorization);
        if (h==null) return null;
        else return new ProxyAuthorizationHeader(h);
    }
    /** Removes ProxyAuthorizationHeader. */
    public void removeProxyAuthorizationHeader()
    {  removeHeader(SipHeaders.Proxy_Authorization);
    }



    //**************************** RFC 2543 Legacy ****************************/


    /** Checks whether the next Route is formed according to RFC2543 Strict Route
     * and adapts the message. */
    public void rfc2543RouteAdapt()
    {  if (hasRouteHeader())
    {  MultipleHeader mrh=getRoutes();
        RouteHeader rh=new RouteHeader(mrh.getTop());
        if (!(new RouteHeader(mrh.getTop())).getNameAddress().getAddress().hasLr())
        {  // re-format the message according to the RFC2543 Strict Route rule
            SipURL next_hop=(new RouteHeader(mrh.getTop())).getNameAddress().getAddress();
            SipURL recipient=getRequestLine().getAddress();
            mrh.removeTop();
            mrh.addBottom(new RouteHeader(new NameAddress(recipient)));
            setRoutes(mrh);
            setRequestLine(new RequestLine(getRequestLine().getMethod(),next_hop));
        }
    }
    }


    /** Changes form RFC2543 Strict Route to RFC3261 Lose Route.
     * <p> The Request-URI is replaced with the last
     * value from the Route header, and that value is removed from the
     * Route header. */
    public void rfc2543toRfc3261RouteUpdate()
    {  // the message is formed according with RFC2543 strict route
        // the next hop is the request-uri
        // the recipient of the message is the last Route value
        RequestLine request_line=getRequestLine();
        SipURL next_hop=request_line.getAddress();
        MultipleHeader mrh=getRoutes();
        SipURL target=(new RouteHeader(mrh.getBottom())).getNameAddress().getAddress();
        mrh.removeBottom();
        next_hop.addLr();
        mrh.addTop(new RouteHeader(new NameAddress(next_hop)));
        removeRoutes();
        addRoutes(mrh);
        setRequestLine(new RequestLine(request_line.getMethod(),target));
    }

}
